package cz.cvut.fit.web.nakupniSeznam.ws;

import java.util.HashMap;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cz.cvut.fit.web.nakupniSeznam.constants.DBConstants;
import cz.cvut.fit.web.nakupniSeznam.constants.WSConstants;
import cz.cvut.fit.web.nakupniSeznam.dao.ItemDAO;
import cz.cvut.fit.web.nakupniSeznam.dao.ListDAO;
import cz.cvut.fit.web.nakupniSeznam.data.Item;
import cz.cvut.fit.web.nakupniSeznam.data.List;
import cz.cvut.fit.web.nakupniSeznam.ws.conv.ErrorConverter;
import cz.cvut.fit.web.nakupniSeznam.ws.conv.InfoConverter;
import cz.cvut.fit.web.nakupniSeznam.ws.conv.ItemConverter;
import cz.cvut.fit.web.nakupniSeznam.ws.conv.ListConverter;
import cz.cvut.fit.web.nakupniSeznam.ws.conv.ListsConverter;

@Path("/list")
public class Lists {
	private static final Logger LOG = LoggerFactory.getLogger(Lists.class);
	public static final String ITEM_SAVE_COMPLETE = "item saving complete";
	public static final String SAVE_COMPLETE = "list saving complete";
	public static final String LOG_OK = "ws succesfully called on list";
	
	@Context
	private UriInfo context;
	private String key;

	//====================== GET ======================//
	

	@GET
	@Produces(WSConstants.WS_PRODUCES)
	/**
	 * Returns informations about user's list (only basic)
	 * @return
	 */
	public Response getUserInfo(@Context HttpHeaders hh) {
		Response.ResponseBuilder builder = Response.status(Response.Status.OK);

		// try to find key in header
		key = AuthLibrary.getKey(hh, builder, LOG);
		if (key == null) {
			return builder.build();
		}

		// check key format
		if (AuthLibrary.checkKeyLength(key, builder, LOG) == false) {
			return builder.build();
		}

		// try to find element in DB
		try {
			java.util.List<List> lists = ListDAO.getInstance().getByOwner(key);
			ListsConverter converter = new ListsConverter(lists,
					context.getAbsolutePath());

			builder.entity(converter);
		} catch (NullPointerException ex) {
			builder.status(Response.Status.NOT_FOUND);
			builder.entity(new InfoConverter(WSConstants.RESULT_NOT_FOUND));
			LOG.error(WSConstants.RESULT_FAILED, ex);
			return builder.build();
		}

		LOG.debug(Lists.LOG_OK);
		return builder.build();
	}

	@Path("{id}")
	@GET
	@Produces(WSConstants.WS_PRODUCES)
	/**
	 * Returns informations about user's list (only basic)
	 * @return
	 */
	public Response getUserList(@Context HttpHeaders hh,
			@PathParam("id") long id) {
		Response.ResponseBuilder builder = Response.status(Response.Status.OK);

		// try to find key in header
		key = AuthLibrary.getKey(hh, builder, LOG);
		if (key == null) {
			return builder.build();
		}

		// check key format
		if (AuthLibrary.checkKeyLength(key, builder, LOG) == false) {
			return builder.build();
		}

		try {
			List list = ListDAO.getInstance().getByOwnerAndId(key, id);
			ListConverter converter = new ListConverter(list,
					context.getAbsolutePath());
			builder.entity(converter);
		} catch (NullPointerException ex) {
			builder.status(Response.Status.NOT_FOUND);
			builder.entity(new InfoConverter(WSConstants.RESULT_NOT_FOUND));
			LOG.error(WSConstants.RESULT_FAILED, ex);
			return builder.build();
		}

		LOG.debug(Lists.LOG_OK);
		return builder.build();
	}

	@Path("{id}/detail")
	@GET
	@Produces(WSConstants.WS_PRODUCES)
	/**
	 * Returns detailed informations about list (with detailed items)
	 * @return
	 */
	public Response getDetailedUserList(@Context HttpHeaders hh,
			@PathParam("id") long id) {
		Response.ResponseBuilder builder = Response.status(Response.Status.OK);

		// try to find key in header
		key = AuthLibrary.getKey(hh, builder, LOG);
		if (key == null) {
			return builder.build();
		}

		// check key format
		if (AuthLibrary.checkKeyLength(key, builder, LOG) == false) {
			return builder.build();
		}

		try {
			List list = ListDAO.getInstance().getByOwnerAndId(key, id);
			java.util.List<Item> items = ItemDAO.getInstance().getByList(
					list.getId());
			ListConverter converter = new ListConverter(list, items,
					context.getAbsolutePath());
			builder.entity(converter);
		} catch (NullPointerException ex) {
			builder.status(Response.Status.NOT_FOUND);
			builder.entity(new InfoConverter(WSConstants.RESULT_NOT_FOUND));
			LOG.error(WSConstants.RESULT_FAILED, ex);
			return builder.build();
		}

		LOG.debug(Lists.LOG_OK);
		return builder.build();
	}

	//====================== POST ======================//
	@POST
	@Produces(WSConstants.WS_PRODUCES)
	@Consumes(WSConstants.WS_CONSUMES)
	/**
	 * Create new list
	 * @return
	 */
	public Response postDetailedUserList(@Context HttpHeaders hh,
			ListConverter converter) {
		Response.ResponseBuilder builder = Response
				.status(Response.Status.CREATED);

		// try to find key in header
		key = AuthLibrary.getKey(hh, builder, LOG);
		if (key == null) {
			return builder.build();
		}

		// check key format
		if (AuthLibrary.checkKeyLength(key, builder, LOG) == false) {
			return builder.build();
		}

		try {
			List list = converter.retrieveRepresentation();
			list.setOwner(key);
			
			//get newly created listId
			Long listId = ListDAO.getInstance().save(list);
			java.util.List<ItemConverter> items = converter.getItems().getItem();
			
			//for mobileIds
			HashMap<Long, Integer> idMap = new HashMap<Long, Integer>();
			Integer listMobileId = converter.getMobileId();
			
			Long id;
			for (ItemConverter i : items) {
				//set all listID to parent list
				i.setListId(listId);
				id = ItemDAO.getInstance().save(i.retrieveRepresentation());
				idMap.put(id, i.getMobileId());
				LOG.debug(Lists.ITEM_SAVE_COMPLETE);
			}
			
			//location header
			builder.header(WSConstants.HEADER_LOCATION, context.getAbsolutePath()
					+ "/" + list.getId());
			
			
			//IF there are some problem with partly incomplete data from DB - set some wait time
			//assign to converter newly created entities right from db, so the result contains entities how they were saved
			converter = new ListConverter(ListDAO.getInstance().getByOwnerAndId(key, listId), ItemDAO.getInstance().getByList(listId), context.getAbsolutePath());
			
			//assign to list all items their saved mobileIds, just for POST calling - no need to keep them in server DB
			converter.setMobileId(listMobileId);
			for (ItemConverter i : converter.getItems().getItem()) {
				i.setMobileId(idMap.get(i.getId()));
			}
			
			builder.entity(converter);
			LOG.debug(Lists.SAVE_COMPLETE);

		} catch (NullPointerException ex) {
			builder.status(Response.Status.BAD_REQUEST);
			builder.entity(new ErrorConverter(DBConstants.DB_BAD_DATA_FORMAT));
			LOG.error(WSConstants.RESULT_FAILED, ex);
			return builder.build();
		}

		LOG.debug(Lists.LOG_OK);
		return builder.build();
	}

	//====================== PUT ======================//

	@Path("{id}")
	@PUT
	@Produces(WSConstants.WS_PRODUCES)
	@Consumes(WSConstants.WS_CONSUMES)
	/**
	 * Create new list
	 * @return
	 */
	public Response putDetailedUserList(@Context HttpHeaders hh,
			ListConverter converter, @PathParam("id") long id) {
		Response.ResponseBuilder builder = Response
				.status(Response.Status.CREATED);

		// try to find key in header
		key = AuthLibrary.getKey(hh, builder, LOG);
		if (key == null) {
			return builder.build();
		}

		// check key format
		if (AuthLibrary.checkKeyLength(key, builder, LOG) == false) {
			return builder.build();
		}

		try {
			List list = converter.retrieveRepresentation();
			list.setOwner(key);

			if (ListDAO.getInstance().getById(id) == null) {
				builder.status(Response.Status.NOT_FOUND);
				builder.entity(new ErrorConverter(WSConstants.RESULT_NOT_FOUND));
				return builder.build();

			} else {
				java.util.List<ItemConverter> items = converter.getItems().getItem();
				Long listId = ListDAO.getInstance().save(list);
				HashMap<Long, Integer> idMap = new HashMap<Long, Integer>();
				Long itemServerId;
				for (ItemConverter i : items) {
					Item item = i.retrieveRepresentation();
					item.setListId(listId);
					itemServerId = ItemDAO.getInstance().save(item);
					idMap.put(itemServerId, i.getMobileId());
					LOG.debug(Lists.ITEM_SAVE_COMPLETE);
				}
				
				// Converter for response
				converter = new ListConverter(ListDAO.getInstance().getByOwnerAndId(key, listId), ItemDAO.getInstance().getByList(listId), context.getAbsolutePath());
				
				//assign to list all items their saved mobileIds
				for (ItemConverter i : converter.getItems().getItem()) {
					i.setMobileId(idMap.get(i.getId()));
				}
				
				builder.header(WSConstants.HEADER_LOCATION,
						context.getAbsolutePath());
				builder.entity(converter);
			}

		} catch (NullPointerException ex) {
			builder.status(Response.Status.BAD_REQUEST);
			builder.entity(new ErrorConverter(DBConstants.DB_BAD_DATA_FORMAT));
			LOG.error(WSConstants.RESULT_FAILED,
					ex);
			return builder.build();
		}

		LOG.debug(Lists.LOG_OK);
		return builder.build();
	}
}
